import pandas as pd
import numpy as np
import os
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix


def load_json_data(filename: str) -> pd.DataFrame:
    """加载JSON数据，处理路径问题和加载错误"""
    current_dir = os.path.dirname(os.path.abspath(__file__))
    file_path = os.path.join(current_dir, "..", "data", filename)
    file_path = os.path.abspath(file_path)

    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            data = pd.read_json(f)
        print(f"成功加载数据: {file_path}，共{len(data)}条记录")
        return data
    except FileNotFoundError:
        print(f"错误: 找不到数据文件 {file_path}")
        return None
    except Exception as e:
        print(f"加载数据时发生错误: {str(e)}")
        return None


# 加载学生数据
df = load_json_data("学生历史成绩及行为特征数据.json")

# 检查数据加载是否成功
if df is None or df.empty:
    print("数据加载失败，无法继续执行程序")
    exit(1)

# 检查必要的列是否存在
required_columns = ['previous_avg_score', 'attendance_rate_avg', 'library_visits_per_week',
                    'participation_in_activities', 'has_scholarship', 'current_score_grade']
missing_columns = [col for col in required_columns if col not in df.columns]

if missing_columns:
    print(f"错误: 数据中缺少必要的列: {missing_columns}")
    print(f"数据中实际包含的列: {df.columns.tolist()}")
    exit(1)

# 数据预处理 - 将成绩等级转换为数值
label_encoder = LabelEncoder()
df['current_score_grade_encoded'] = label_encoder.fit_transform(df['current_score_grade'])

# 特征和目标变量
X = df[['previous_avg_score', 'attendance_rate_avg', 'library_visits_per_week',
        'participation_in_activities', 'has_scholarship']]
y = df['current_score_grade_encoded']

# 数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 训练模型
dt_model = DecisionTreeClassifier(random_state=42)
rf_model = RandomForestClassifier(random_state=42, n_estimators=100)

dt_model.fit(X_train, y_train)
rf_model.fit(X_train, y_train)

# 预测
y_pred_dt = dt_model.predict(X_test)
y_pred_rf = rf_model.predict(X_test)

# 评估指标
accuracy_dt = round(accuracy_score(y_test, y_pred_dt), 3)
accuracy_rf = round(accuracy_score(y_test, y_pred_rf), 3)

# 混淆矩阵
cm_dt = confusion_matrix(y_test, y_pred_dt)
cm_rf = confusion_matrix(y_test, y_pred_rf)

# 分类报告
report_dt = classification_report(y_test, y_pred_dt, target_names=label_encoder.classes_, output_dict=True)
report_rf = classification_report(y_test, y_pred_rf, target_names=label_encoder.classes_, output_dict=True)

# 特征重要性
feature_importance = pd.DataFrame({
    "feature": X.columns,
    "dt_importance": dt_model.feature_importances_,
    "rf_importance": rf_model.feature_importances_
}).sort_values("rf_importance", ascending=False)

# 生成预测数据（模拟未来学生数据）
future_students = pd.DataFrame({
    'previous_avg_score': [85.2, 72.5, 68.3, 92.1, 76.8],
    'attendance_rate_avg': [0.92, 0.75, 0.60, 0.95, 0.82],
    'library_visits_per_week': [4, 2, 1, 5, 3],
    'participation_in_activities': [3, 1, 2, 4, 3],
    'has_scholarship': [1, 0, 0, 1, 1]
})

# 预测未来学生成绩
future_pred_dt = label_encoder.inverse_transform(dt_model.predict(future_students))
future_pred_rf = label_encoder.inverse_transform(rf_model.predict(future_students))

# 封装结果
student_pred_result = {
    "model_metrics": {
        "decision_tree": {
            "accuracy": accuracy_dt,
            "classification_report": report_dt
        },
        "random_forest": {
            "accuracy": accuracy_rf,
            "classification_report": report_rf
        }
    },
    "confusion_matrix": {
        "decision_tree": cm_dt.tolist(),
        "random_forest": cm_rf.tolist(),
        "classes": label_encoder.classes_.tolist()
    },
    "feature_importance": feature_importance.to_dict("records"),
    "future_prediction": {
        "students_features": future_students.to_dict("records"),
        "dt_prediction": future_pred_dt.tolist(),
        "rf_prediction": future_pred_rf.tolist()
    }
}

print("学生成绩预测模型训练完成")
